package com.yt.fras.controller;


import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yt.fras.pojo.base.JsonResult;
import com.yt.fras.pojo.base.LoginUser;
import com.yt.fras.pojo.base.UserOutputExcel;
import com.yt.fras.pojo.entity.Attendance;
import com.yt.fras.pojo.entity.AttendanceUser;
import com.yt.fras.pojo.entity.ManagementGroup;
import com.yt.fras.pojo.model.Location;
import com.yt.fras.pojo.model.UserInfo;
import com.yt.fras.service.*;
import com.yt.fras.utils.*;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import static com.yt.fras.constant.AttendanceStatus.*;

/**
 * <p>
 * 考勤管理模块
 * </p>
 *
 * @author yt
 * @since 2021-10-25
 */
@RestController
@Api(tags = "考勤管理模块")
@RequestMapping("/fras")
public class AttendanceController {

    @Autowired
    private AttendanceService attendanceService;

    @Autowired
    private AttendanceUserService attendanceUserService;

    @Autowired
    private ManagementGroupService managementGroupService;

    @Autowired
    private UserService userService;

    @Autowired
    private ThreadPoolTaskExecutor applicationTaskExecutor;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private LocationUtil locationUtil;

    @Autowired
    private MailService mailService;

    /**
     * 获取用户所在用户组的所有考勤记录
     *
     * @param groupId
     * @return
     */
    @GetMapping("/user/getUserAttendancesByGroup/{groupId}")
    @ApiOperation(value = "获取用户所在用户组的所有考勤记录")
    @ApiResponses({
            @ApiResponse(code = 200, message = "获取成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "groupId", value = "用户组Id", required = true)
    })
    public String getUserAttendancesByGroup(@PathVariable String groupId) {
        JsonResult result;
        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (managementGroupService.getById(groupId) == null) {
            result = ResultTool.fail();
            result.setMsg("用户组不存在！");
            return JSON.toJSONString(result);
        }

        result = ResultTool.success(attendanceUserService.getUserAttendancesByGroup(loginUser.getId(), groupId));
        result.setMsg("获取成功！");
        return JSON.toJSONString(result);
    }

    /**
     * 获取考勤记录里面所有用户的考勤信息
     *
     * @param attendanceId
     * @return
     */
    @GetMapping("/admin/getUserAttendancesByAttendance/{attendanceId}")
    @ApiOperation(value = "获取考勤记录里面所有用户的考勤信息")
    @ApiResponses({
            @ApiResponse(code = 200, message = "获取成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "attendanceId", value = "考勤Id", required = true)
    })
    public String getUserAttendancesByAttendance(@PathVariable String attendanceId) {
        JsonResult result;
        if (attendanceService.getById(attendanceId) == null) {
            result = ResultTool.fail();
            result.setMsg("考勤记录不存在！");
            return JSON.toJSONString(result);
        }
        result = ResultTool.success(attendanceUserService.getUserAttendancesByAttendanceId(attendanceId));
        result.setMsg("获取成功！");
        return JSON.toJSONString(result);
    }

    /**
     * 查询用户组的所有考勤记录
     *
     * @param groupId
     * @return
     */
    @GetMapping("/admin/getAttendancesByGroup/{groupId}")
    @ApiOperation(value = "查询用户组的所有考勤记录")
    @ApiResponses({
            @ApiResponse(code = 200, message = "获取成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "groupId", value = "用户组Id", required = true)
    })
    public String getAttendancesByGroup(@PathVariable String groupId) {
        JsonResult result;
        if (managementGroupService.getById(groupId) == null) {
            result = ResultTool.fail();
            result.setMsg("用户组不存在！");
            return JSON.toJSONString(result);
        }
        result = ResultTool.success(attendanceService.getAttendancesByGroup(groupId));
        result.setMsg("获取成功！");
        return JSON.toJSONString(result);
    }

    /**
     * 查询用户组未开启和正在开启的考勤记录
     *
     * @param groupId
     * @return
     */
    @GetMapping("/user/getEnableAttendancesByGroup/{groupId}")
    @ApiOperation(value = "查询用户组未开启和正在开启的考勤记录")
    @ApiResponses({
            @ApiResponse(code = 200, message = "获取成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "groupId", value = "用户组Id", required = true)
    })
    public String getEnableAttendancesByGroup(@PathVariable String groupId) {
        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        JsonResult result;
        if (managementGroupService.getById(groupId) == null) {
            result = ResultTool.fail();
            result.setMsg("用户组不存在！");
            return JSON.toJSONString(result);
        }
        result = ResultTool.success(attendanceService.getEnableAttendancesByGroup(groupId, loginUser.getId()));
        result.setMsg("获取成功！");
        return JSON.toJSONString(result);
    }


    /**
     * 添加考勤记录
     *
     * @param groupId
     * @param description
     * @param startTime
     * @param endTime
     * @param request
     * @return
     * @throws ParseException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    @PostMapping("/admin/attendance/{groupId}/{description}/{startTime}/{endTime}")
    @ApiOperation(value = "添加考勤记录")
    @ApiResponses({
            @ApiResponse(code = 0, message = "添加失败！"),
            @ApiResponse(code = 200, message = "添加成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "groupId", value = "组Id", required = true),
            @ApiImplicitParam(paramType = "path", name = "description", value = "考勤描述", required = true),
            @ApiImplicitParam(paramType = "path", name = "startTime", value = "开始时间", required = true),
            @ApiImplicitParam(paramType = "path", name = "endTime", value = "结束时间", required = true)
    })
    public String addAttendance(@PathVariable String groupId, @PathVariable String description, @PathVariable String startTime, @PathVariable String endTime, HttpServletRequest request) throws ParseException, ExecutionException, InterruptedException {
        JsonResult result;

        if (managementGroupService.getById(groupId) == null) {
            result = ResultTool.fail();
            result.setMsg("用户组不存在！");
            return JSON.toJSONString(result);
        }
        //根据IP地址获取经纬度
        Location ipLocation = locationUtil.getIpLocation(request.getHeader("X-Real-IP"));
        if (ipLocation.getLocation().equals(",")) {
            result = ResultTool.fail();
            result.setMsg("IP定位错误！");
            return JSON.toJSONString(result);
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date start = simpleDateFormat.parse(startTime);
        Date end = simpleDateFormat.parse(endTime);
        Date current = new Date();
        ManagementGroup managementGroup = managementGroupService.getById(groupId);

        Attendance attendance = new Attendance();
        attendance.setDescription(description);
        attendance.setGroupId(groupId);

        //根据当前时间判断考勤状态
        if (current.getTime() < start.getTime()) {
            attendance.setStatus(WAITING);
        } else if (current.getTime() > end.getTime()) {
            attendance.setStatus(ENDING);
        } else {
            attendance.setStatus(RUNNING);
        }
        attendance.setActualCount(0);
        attendance.setTotalCount(managementGroup.getCount());
        attendance.setStartTime(start);
        attendance.setEndTime(end);

        if (attendanceService.save(attendance)) {
            List<UserInfo> userInfoList = userService.getUserInfoListByGroup(groupId);
            List<AttendanceUser> attendanceUserList = new ArrayList<>();
            //利用线程池任务异步添加用户考勤数据
            CompletableFuture<Void> attendanceUsersFuture = CompletableFuture.runAsync(() -> {
                userInfoList.forEach(userInfo -> {
                    AttendanceUser attendanceUser = new AttendanceUser();
                    attendanceUser.setAttendanceId(attendance.getId());
                    attendanceUser.setUserId(userInfo.getId());
                    attendanceUser.setStatus(ABSENTEEISM);
                    attendanceUser.setAttendanceTime(new Date());
                    attendanceUserList.add(attendanceUser);
                });
            }, applicationTaskExecutor);

            CompletableFuture.allOf(attendanceUsersFuture).get();
            //将所有的用户考勤数据写入数据库
            attendanceUserService.saveBatch(attendanceUserList);

            String[] location = ipLocation.getLocation().split(",");
            redisUtil.hSet(attendance.getId(), "longitude", location[0]);
            redisUtil.hSet(attendance.getId(), "latitude", location[1]);

            result = ResultTool.success();
            result.setMsg("添加成功！");
        } else {
            result = ResultTool.fail();
            result.setMsg("添加失败！");
        }

        return JSON.toJSONString(result);
    }

    /**
     * 修改考勤记录
     *
     * @param attendanceId
     * @param description
     * @param startTime
     * @param endTime
     * @param request
     * @return
     * @throws ParseException
     */
    @PutMapping("/admin/attendance/{attendanceId}/{description}/{startTime}/{endTime}")
    @ApiOperation(value = "修改考勤记录")
    @ApiResponses({
            @ApiResponse(code = 0, message = "修改失败！"),
            @ApiResponse(code = 200, message = "修改成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "attendanceId", value = "考勤Id", required = true),
            @ApiImplicitParam(paramType = "path", name = "description", value = "考勤描述", required = true),
            @ApiImplicitParam(paramType = "path", name = "startTime", value = "开始时间", required = true),
            @ApiImplicitParam(paramType = "path", name = "endTime", value = "结束时间", required = true)
    })
    public String updateAttendance(@PathVariable String attendanceId, @PathVariable String description, @PathVariable String startTime, @PathVariable String endTime, HttpServletRequest request) throws ParseException {
        JsonResult result;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Attendance attendance = attendanceService.getById(attendanceId);
        if (attendance == null) {
            result = ResultTool.fail();
            result.setMsg("考勤记录不存在！");
            return JSON.toJSONString(result);
        }
        Date start = simpleDateFormat.parse(startTime);
        Date end = simpleDateFormat.parse(endTime);
        Date current = new Date();

        //根据当前时间判断考勤状态
        if (current.getTime() < start.getTime()) {
            attendance.setStatus(1);
        } else if (current.getTime() > end.getTime()) {
            attendance.setStatus(0);
        } else {
            attendance.setStatus(2);
        }
        attendance.setDescription(description);
        attendance.setStartTime(start);
        attendance.setEndTime(end);

        if (attendanceService.updateById(attendance)) {
            result = ResultTool.success();
            result.setMsg("修改成功！");
        } else {
            result = ResultTool.fail();
            result.setMsg("修改失败！");
        }

        return JSON.toJSONString(result);
    }

    /**
     * 删除考勤记录
     *
     * @param attendanceId
     * @return
     * @throws ExecutionException
     * @throws InterruptedException
     */
    @DeleteMapping("/admin/attendance/{attendanceId}")
    @ApiOperation(value = "删除考勤记录")
    @ApiResponses({
            @ApiResponse(code = 0, message = "删除失败！"),
            @ApiResponse(code = 200, message = "删除成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "attendanceId", value = "考勤Id", required = true),
    })
    public String deleteAttendance(@PathVariable String attendanceId) throws ExecutionException, InterruptedException {
        JsonResult result;

        Attendance attendance = attendanceService.getById(attendanceId);

        if (attendance == null) {
            result = ResultTool.fail();
            result.setMsg("考勤记录不存在！");
            return JSON.toJSONString(result);
        }

        if (attendanceService.removeById(attendanceId)) {
            QueryWrapper<AttendanceUser> wrapper = new QueryWrapper<>();
            wrapper.eq("attendance_id", attendanceId);
            List<AttendanceUser> list = attendanceUserService.list(wrapper);
            List<String> attendanceUserIds = new ArrayList<>();

            //利用线程池任务异步获取用户考勤数据ID
            CompletableFuture<Void> attendanceUsersFuture = CompletableFuture.runAsync(() -> {
                list.forEach(attendanceUser -> {
                    attendanceUserIds.add(attendanceUser.getId());
                });
            }, applicationTaskExecutor);

            CompletableFuture.allOf(attendanceUsersFuture).get();

            //删除经纬度
            redisUtil.hDel(attendanceId, "latitude", "longitude");

            //删除该考勤所有用户考勤记录
            if (attendanceUserService.removeByIds(attendanceUserIds)) {
                result = ResultTool.success();
                result.setMsg("删除成功！");
            } else {
                result = ResultTool.fail();
                result.setMsg("删除失败！");
            }
        } else {
            result = ResultTool.fail();
            result.setMsg("删除失败！");
        }

        return JSON.toJSONString(result);
    }

    /**
     * 提前结束考勤
     *
     * @param attendanceId
     * @return
     */
    @PostMapping("/admin/stopAttendance/{attendanceId}")
    @ApiOperation(value = "提前结束考勤")
    @ApiResponses({
            @ApiResponse(code = 0, message = "提前结束失败！"),
            @ApiResponse(code = 200, message = "提前结束成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "attendanceId", value = "考勤Id", required = true)
    })
    public String stopAttendance(@PathVariable String attendanceId) {
        JsonResult result;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Attendance attendance = attendanceService.getById(attendanceId);
        if (attendance == null) {
            result = ResultTool.fail();
            result.setMsg("考勤记录不存在！");
            return JSON.toJSONString(result);
        }

        //更新考勤状态
        attendance.setStatus(0);
        attendance.setEndTime(new Date());

        ManagementGroup managementGroup = managementGroupService.getById(attendance.getGroupId());
        List<UserInfo> userInfoList = userService.getUserInfoListByGroup(attendance.getGroupId());

        if (attendanceService.updateById(attendance)) {
            result = ResultTool.success();
            result.setMsg("提前结束成功！");
            redisUtil.hDel(attendanceId, "latitude", "longitude");

            //向所有用户发送提前结束考勤邮件通知
            userInfoList.forEach(userInfo -> mailService.sendHtmlMail(userInfo.getEmail(), "考勤提前结束", SetContent.remindEnd(userInfo.getUsername(), managementGroup.getName(), simpleDateFormat.format(attendance.getEndTime()), attendance.getDescription())));
        } else {
            result = ResultTool.fail();
            result.setMsg("提前结束失败！");
        }

        return JSON.toJSONString(result);
    }


    /**
     * 修改用户考勤记录
     *
     * @param attendanceUserId
     * @param status
     * @return
     */
    @PutMapping("/admin/changeAttendanceStatus/{attendanceUserId}/{status}")
    @ApiOperation(value = "修改用户考勤记录")
    @ApiResponses({
            @ApiResponse(code = 0, message = "修改失败！"),
            @ApiResponse(code = 200, message = "修改成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "attendanceUserId", value = "用户考勤Id", required = true),
            @ApiImplicitParam(paramType = "path", name = "status", value = "考勤状态", required = true)
    })
    public String changeAttendanceStatus(@PathVariable String attendanceUserId, @PathVariable String status) {
        JsonResult result;

        System.out.println("status = " + status);

        AttendanceUser attendanceUser = attendanceUserService.getById(attendanceUserId);
        Attendance attendance = attendanceService.getById(attendanceUser.getAttendanceId());

        if (attendance == null) {
            result = ResultTool.fail();
            result.setMsg("考勤记录不存在！");
            return JSON.toJSONString(result);
        }

        if (attendanceUser == null) {
            result = ResultTool.fail();
            result.setMsg("用户考勤记录不存在！");
            return JSON.toJSONString(result);
        }

        //判断更改状态
        if (status.equals(ATTENDANCE)) {
            if (!attendanceUser.getStatus().equals(ATTENDANCE)) {
                attendanceUser.setStatus(status);
                attendance.setActualCount(attendance.getActualCount() + 1);
                attendanceUser.setAttendanceTime(new Date());
            }
        } else {
            if (attendanceUser.getStatus().equals(ATTENDANCE)) {
                attendance.setActualCount(attendance.getActualCount() - 1);
            }
            attendanceUser.setStatus(status);
        }


        if (attendanceUserService.updateById(attendanceUser) && attendanceService.updateById(attendance)) {
            result = ResultTool.success();
            result.setMsg("修改成功！");
        } else {
            result = ResultTool.fail();
            result.setMsg("修改失败！");
        }

        return JSON.toJSONString(result);
    }


    /**
     * 获取考勤定位
     *
     * @param attendanceId
     * @return
     */
    @GetMapping("/user/getAttendanceIpLocation/{attendanceId}")
    @ApiOperation(value = "获取考勤定位")
    @ApiResponses({
            @ApiResponse(code = 0, message = "获取失败！"),
            @ApiResponse(code = 200, message = "获取成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "attendanceId", value = "考勤Id", required = true)
    })
    public String getAttendanceIpLocation(@PathVariable String attendanceId) {
        JsonResult result;
        Attendance attendance = attendanceService.getById(attendanceId);
        if (attendance == null) {
            result = ResultTool.fail();
            result.setMsg("考勤记录不存在！");
            return JSON.toJSONString(result);
        }
        //判断获取经纬度是否为空
        if (Optional.ofNullable(redisUtil.hGet(attendanceId, "latitude")).isPresent() && Optional.ofNullable(redisUtil.hGet(attendanceId, "longitude")).isPresent()) {
            result = ResultTool.success();
            result.setMsg("获取成功！");
            Map<String, Object> map = new HashMap<>();
            map.put("latitude", redisUtil.hGet(attendanceId, "latitude"));
            map.put("longitude", redisUtil.hGet(attendanceId, "longitude"));
            result.setData(map);
        } else {
            result = ResultTool.fail();
            result.setMsg("获取失败！");
        }

        return JSON.toJSONString(result);
    }

    /**
     * 导出缺勤用户
     *
     * @param attendanceId
     * @return
     */
    @GetMapping("/admin/outputAbsentUsers/{attendanceId}")
    @ApiOperation(value = "导出缺勤用户")
    @ApiResponses({
            @ApiResponse(code = 0, message = "导出失败！"),
            @ApiResponse(code = 200, message = "导出成功！"),
            @ApiResponse(code = 500, message = "系统出现错误~")
    })
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "attendanceId", value = "考勤Id", required = true)
    })
    public String outputAbsentUsers(HttpServletResponse response, @PathVariable String attendanceId) {
        JsonResult result;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Attendance attendance = attendanceService.getById(attendanceId);
        if (attendance == null) {
            result = ResultTool.fail();
            result.setMsg("考勤记录不存在！");
            return JSON.toJSONString(result);
        }
        List<UserOutputExcel> userOutputExcels = attendanceUserService.getAbsentUsers(attendanceId);
        response.setCharacterEncoding("utf-8");
        String fileName = UrlEncodeUtil.getURLEncoderString(attendance.getDescription() + "-缺勤人员-" + simpleDateFormat.format(new Date()));
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        try {
            EasyExcel.write(response.getOutputStream(), UserOutputExcel.class).sheet("模板").doWrite(userOutputExcels);
            result = ResultTool.success();
            result.setMsg("导出成功！");
        } catch (Exception e) {
            e.printStackTrace();
            result = ResultTool.fail();
            result.setMsg("导出失败！");
        }
        return JSON.toJSONString(result);
    }

}

